package com.cya.lotterysystem.service.mq;

import cn.hutool.core.date.DateUtil;
import com.cya.lotterysystem.common.config.ExecutorConfig;
import com.cya.lotterysystem.common.exception.ServiceException;
import com.cya.lotterysystem.common.utils.JacksonUtil;
import com.cya.lotterysystem.common.utils.MailUtil;
import com.cya.lotterysystem.controller.param.DrawPrizeParam;
import com.cya.lotterysystem.dao.dataobject.WinnerRecordDO;
import com.cya.lotterysystem.dao.mapper.ActivityPrizeMapper;
import com.cya.lotterysystem.dao.mapper.WinnerRecordMapper;
import com.cya.lotterysystem.service.DrawPrizeService;
import com.cya.lotterysystem.service.activityStatus.ActivityStatusManager;
import com.cya.lotterysystem.service.dto.ActivityPrizeDO;
import com.cya.lotterysystem.service.dto.ConverActivityStatusDTO;
import com.cya.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.cya.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.cya.lotterysystem.service.enums.ActivityStatusEnum;
import com.cya.lotterysystem.service.enums.ActivityUserStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.cya.lotterysystem.common.config.DirectRabbitConfig.QUEUE_NAME;

/**
 * @author ：陈奕安（3048279304@qq.com）
 * @date ：Created in 2025/11/7 9:47
 * @description：
 * @modified By：
 * @version:
 */
@Component
@Slf4j
@RabbitListener(queues = QUEUE_NAME)//监听该队列的消息
public class MqReceiver {

    @Autowired
    private DrawPrizeService drawPrizeService;

    @Autowired
    private ActivityStatusManager activityStatusManager;

    @Autowired
    private MailUtil mailUtil;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private WinnerRecordMapper winnerRecordMapper;


    @RabbitHandler
    public void process(Map<String,String>message) throws Exception {
//        成功接收队列的消息
        log.info("MQ成功接收到消息。Messgae：{}", JacksonUtil.writeValueAsString(message));
        String paraString = message.get("messageData");

        DrawPrizeParam param = JacksonUtil.readValue(paraString, DrawPrizeParam.class);
//        处理抽奖路程

try {

    //校验抽奖请求是否有效

if (!drawPrizeService.checkDrawPrizeParam(param)){
    return;
}
//        状态扭转处理（设计模式)
    statusConbert( param);

//        保存中奖者名单
    List<WinnerRecordDO>  winnerRecordDOList=
            drawPrizeService.saveWinnerRecords( param);


//        邮箱通知中奖者
    //抽奖之后后续流程：异步（并发处理）
    syscExecute(winnerRecordDOList);



}catch (ServiceException e){

    log.error("处理MQ消息异常：{}:{}",e.getCode(),e.getMessage(),e);
//        如果异常，需保证事务一致性，再抛出异常
        rollback(param);
//抛出异常，消息重试（解决异常：代码bug，网络问题，服务问题）
    throw e;
} catch (Exception e){
//        如果异常，需保证事务一致性，再抛出异常

    log.error("处理MQ消息异常！",e);

        throw e;
}



//        如果异常，需保证事务一致性，再抛出异常




    }

    /**
     * 处理抽奖异常回滚行为：回复处理请求之前的库表状态
     * @param param
     */
    private void rollback(DrawPrizeParam param) {
        //1. 回滚状态，活动，奖品，人员
        //状态是否需要回滚
        if (!statysNeedRollback(param)){
            //不需要，return

            return;
        }

        //需要回滚
        rollbackStatus(param);

        //2.回滚中奖者名单
        //是否需要回滚
        if (!winnerNeedRollback(param)){
            //不需要 return
            
            return;
        }
        //需要回滚
        rollbackWinner(param);
    }

    /**
     * 回滚中奖记录，删除奖品下的中奖者
     * @param param
     */
    private void rollbackWinner(DrawPrizeParam param) {

        drawPrizeService.deleteRecord(param.getActivityId(),param.getPrizeId());
    }

    /**
     * 判断活动中检品是否存在中奖者
     * @param param
     * @return
     */
    private boolean winnerNeedRollback(DrawPrizeParam param) {

       int count= winnerRecordMapper.countByAPId(param.getActivityId(),param.getPrizeId());
       return count>0;
    }

    /**
     * 回复相关状态
     * @param param
     */
    private void rollbackStatus(DrawPrizeParam param) {
        ConverActivityStatusDTO converActivityStatusDTO=new ConverActivityStatusDTO();
        converActivityStatusDTO.setActivityId(param.getActivityId());
        converActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.RUNNING);
        converActivityStatusDTO.setPrizeId(param.getPrizeId());
        converActivityStatusDTO.setTargetPrizeStatus(ActivityPrizeStatusEnum.INIT);
        converActivityStatusDTO.setUserIds(
                param.getWinnerList().stream().map(
                        DrawPrizeParam.Winner::getUserId
                ).collect(Collectors.toList())
        );
        converActivityStatusDTO.setTargetUserStatus(ActivityUserStatusEnum.INIT);

        activityStatusManager.rollbackhandlerEvent(converActivityStatusDTO);
    }

    private boolean statysNeedRollback(DrawPrizeParam param) {
        //判断活动+奖品+人员相关状态是否扭转
        //扭转状态时，保证了事物的一致性，
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());

        if (activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())){
            //已经扭转需要回滚
            return true;
        }

        return false;
    }

    /**
     * 并发处理抽奖后续流程
     * @param winnerRecordDOList
     */
    private void syscExecute(List<WinnerRecordDO> winnerRecordDOList) {
        //邮件通知
        threadPoolTaskExecutor.execute(()->sendMail(winnerRecordDOList));
    }

    /**
     *发送邮件
     * @param winnerRecordDOList
     */
    private void sendMail(List<WinnerRecordDO> winnerRecordDOList) {
        if (CollectionUtils.isEmpty(winnerRecordDOList)){
            log.info("中奖列表为空，不用发邮件！");
            return;
        }
        for (WinnerRecordDO winnerRecordDO:winnerRecordDOList){
            String contex="哈喽,尊敬的拼多多用户"
                    +winnerRecordDO.getWinnerName()
                    +",恭喜你在拼多多"+winnerRecordDO.getActivityName()
                    +"活动中获得"+ ActivityPrizeTiersEnum.forName(winnerRecordDO.getPrizeTier()).getMessage()
                    +"。获奖时间为"+ DateUtil.formatTime(winnerRecordDO.getWinningTime())
                    +"，请尽快领取!";
            mailUtil.sendSampleMail(winnerRecordDO.getWinnerEmail(),"拼多多中奖通知",
                    contex);

        }

    }

    /**
     * 状态扭转
     * @param param
     */
    private void statusConbert(DrawPrizeParam param){
        ConverActivityStatusDTO converActivityStatusDTO=new ConverActivityStatusDTO();
        converActivityStatusDTO.setActivityId(param.getActivityId());
        converActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.COMPLETED);
        converActivityStatusDTO.setPrizeId(param.getPrizeId());
        converActivityStatusDTO.setTargetPrizeStatus(ActivityPrizeStatusEnum.COMPLETED);
        converActivityStatusDTO.setUserIds(param.getWinnerList().stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .collect(Collectors.toList()));
        converActivityStatusDTO.setTargetUserStatus(ActivityUserStatusEnum.COMPLETED);

    activityStatusManager.handlerEvent(converActivityStatusDTO);
    }

//    private void statusConbert(DrawPrizeParam param) {
        //活动：RUNNING-->COMPLETER?? 奖品全部抽完之后，所有的奖品状态改变之后才该改变
//        奖品 INIT-COMPLETED
//        人员 INIT-->COMPLETED、

// 问题：1.活动状态扭转有依赖性，导致代码维护性差
        //2.状态扭转套件可能会扩展，当前写法扩展性差，维护性差
        //3. 代码灵活性差，扩展性，维护性差
        //解决方案：设计模式，（责任链模式，策略模式）


//        1 扭转奖品状态
//        查询活动关联奖品信息
//        判断当前状态是否符合扭转奖品状态，判断当前是否为为COMPLETED，如果不是，则扭转

//        2 扭转人员状态
        //        查询活动关联奖品信息
//        判断当前状态是否符合扭转人员状态，判断当前是否为为COMPLETED，如果不是，则扭转
//        4 扭转xxx状态


//       3 扭转活动状态依赖奖品状态
        //        查询活动关联奖品信息
//        判断当前状态是否符合扭转活动状态，判断当前是否为为COMPLETED，如果不是，且全部奖品抽完后，且xxx状态扭转则扭转



//        4更新缓完整信息缓存存

//    }
}
